#include "data_flood.h"
#include "db_struct.h"

#define N_SUMS 32
#define FNV_PRIME 16777619
#define UINT16_MAX		(65535)

uint32 CHECK_STEP = 2;

static const uint32 g_checksumBaseOffsets[N_SUMS] = {0x5B1F36E9,
    0xB8525960,
    0x02AB50AA,
    0x1DE66D2A,
    0x79FF467A,
    0x9BB9F8A3,
    0x217E7CD2,
    0x83E13D2C,
    0xF8D4474F,
    0xE39EB970,
    0x42C6AE16,
    0x993216FA,
    0x7B093B5D,
    0x98DAFF3C,
    0xF718902A,
    0x0B1C9CDB,
    0xE58F764B,
    0x187636BC,
    0x5D7B3BB1,
    0xE73DE7DE,
    0x92BEC979,
    0xCCA6C0B2,
    0x304A0979,
    0x85AA43D4,
    0x783125BB,
    0x6CA8EAA2,
    0xE407EAC6,
    0x4B5CFC3E,
    0x9FBF8C76,
    0x15CA20BE,
    0xF2CA9FD3,
    0x959BD756};

static inline OffsetNumber PageGetMaxOffsetNumber(char* pghr)
{
    OffsetNumber maxoff = 0;
    uint64 pageheadersize = SizeOfHeapPageHeaderData;

    if (((HeapPageHeader)pghr)->pd_lower <= pageheadersize)
        maxoff = 0;
    else
        maxoff = (((HeapPageHeader)pghr)->pd_lower - pageheadersize) / sizeof(ItemIdData);

    return maxoff;
}

static inline uint32 pg_checksum_init(uint32 seed, uint32 value)
{
    CHECKSUM_COMP(seed, value);
    return seed;
}

static uint32 pg_checksum_block(char* data, uint32 size)
{
    uint32 sums[N_SUMS];
    uint32* dataArr = (uint32*)data;
    uint32 result = 0;
    uint32 i, j;

    /* initialize partial checksums to their corresponding offsets */
    for (j = 0; j < N_SUMS; j += CHECK_STEP) {
        sums[j] = pg_checksum_init(g_checksumBaseOffsets[j], dataArr[j]);
        sums[j + 1] = pg_checksum_init(g_checksumBaseOffsets[j + 1], dataArr[j + 1]);
    }
    dataArr += N_SUMS;

    /* main checksum calculation */
    for (i = 1; i < size / (sizeof(uint32) * N_SUMS); i++) {
        for (j = 0; j < N_SUMS; j += CHECK_STEP) {
            CHECKSUM_COMP(sums[j], dataArr[j]);
            CHECKSUM_COMP(sums[j + 1], dataArr[j + 1]);
        }
        dataArr += N_SUMS;
    }

    /* finally add in two rounds of zeroes for additional mixing */
    for (j = 0; j < N_SUMS; j++) {
        CHECKSUM_COMP(sums[j], 0);
        CHECKSUM_COMP(sums[j], 0);

        /* xor fold partial checksums together */
        result ^= sums[j];
    }

    return result;
}

static uint16 pg_checksum_page(char* page, uint32 blkno)
{
    HeapPageHeader phdr = (HeapPageHeader)page;
    uint32 checksum;

    phdr->pd_checksum = 0;
    checksum = pg_checksum_block(page, PAGE_SIZE);

    checksum ^= blkno;
    return (checksum % UINT16_MAX) + 1;
}

static void page_fill_unique(uint64 *unique_num, char* page_buff, uint32 unique_type)
{
    HeapPageHeader *tup_head= (HeapPageHeader*)page_buff;
    char* value_addr = NULL;

    if(!user_arg.unique_column)
        return;

    int max_offset = PageGetMaxOffsetNumber(page_buff);

    if(user_arg.unique_no != 0)
        df_error("user_arg.unique_no != 0");
    for(int loop = 1; loop < max_offset; loop++) {
        int64 temp_value = (int64)*unique_num;
        *unique_num = *unique_num + 1;

        value_addr = read_tup_realdata(page_buff, loop);
        if(unique_type == DF_INT8OID) {
            *((int64*)value_addr) = (int64)temp_value;
        } else if(unique_type == DF_INT4OID) {
            *((int*)value_addr) = (int)temp_value;
        }
    }
}

/* 
 * unique_num为唯一性数据填充的累加值。
 * unique_type为唯一性列的列类型
 */
void page_hack(char* buff, int pages, uint32 pageno, uint64 *unique_num, uint32 unique_type)
{
    HeapPageHeader ph = NULL;

    for(int loop = 0; loop < pages; loop++) {
        page_fill_unique(unique_num, buff + loop * PAGE_SIZE, unique_type);
        ph = (HeapPageHeader)(buff + loop * PAGE_SIZE);
        ph->pd_checksum = pg_checksum_page((char*)ph, pageno + loop);
    }
}

/* 读取一个page第offnum个tuple，返回的为真实用户数据的首地址 */
char* read_tup_realdata(char* page_buf, int offnum)
{
    ItemIdData *iid = NULL;
    HeapTupleHeaderData *tup_data = NULL;
    iid = HeapPageGetItemId(page_buf, offnum);
    tup_data = (HeapTupleHeaderData *)PageGetItem(page_buf, iid);

    return (char*)tup_data + tup_data->t_hoff;
}